#include "dominion.h"
#include "dominion_helpers.h"
#include "sput/sput.h"
#include "rngs.h"
#include <stdlib.h>

#define TEST_RUNS 1000
#define TEST_CARD adventurer

struct gameState *testState;

static void test_card(void)
{
    int i;
    int choice1 = -1;
    int choice2 = -1;
    int choice3 = -1;
    int *bonus = NULL;

    int init_hand_count, post_hand_count;
    int init_hand_treasure, post_hand_treasure;
    int init_deck_count, post_deck_count;
    int init_deck_treasure, post_deck_treasure;
    int init_discard_count, post_discard_count;
    int init_discard_treasure, post_discard_treasure;
    int init_total_treasure, post_total_treasure;
    int init_total_count, post_total_count;
    int treasure_increase;
    int init_game_total_count, post_game_total_count;
    int init_game_counts[MAX_DECK], post_game_counts[MAX_DECK];

    int run;
    int res;
    int player;
    int handPos;
    int *hand;
    int *deck;
    int *discard;

    for(run = 0; run < TEST_RUNS; run++) {
        randState(testState, adventurer, action);
        player = testState->whoseTurn;

        hand = testState->hand[player];
        deck = testState->deck[player];
        discard = testState->discard[player];

        init_hand_count = testState->handCount[player];
        init_deck_count = testState->deckCount[player];
        init_discard_count = testState->discardCount[player];
        init_total_count = init_hand_count + init_deck_count + init_discard_count;

        handPos = get_first_in_pile(hand, init_hand_count, TEST_CARD);

        init_hand_treasure = count_cards_of_type(hand, init_hand_count, treasure_t);
        init_deck_treasure = count_cards_of_type(deck, init_deck_count, treasure_t);
        init_discard_treasure = count_cards_of_type(discard, init_discard_count, treasure_t);
        init_total_treasure = init_hand_treasure + init_deck_treasure + init_discard_treasure;

        init_game_total_count = get_total_card_counts(testState, init_game_counts);

        // Set maximum treasure count increase to the lower of 2 or the initial
        // number of treasure cards in the deck
        treasure_increase = init_discard_treasure + init_deck_treasure > 2 ? 2 : init_discard_treasure + init_deck_treasure;

        /* PLAY CARD */
        res = cardEffect(TEST_CARD, choice1, choice2, choice3, testState, handPos, bonus);

        // This probably isn't necessary, given that each pile variable is a
        // pointer, and the value of the pointers should not have changed...
        hand = testState->hand[player];
        deck = testState->deck[player];
        discard = testState->discard[player];

        post_hand_count = testState->handCount[player];
        post_deck_count = testState->deckCount[player];
        post_discard_count = testState->discardCount[player];
        post_total_count = post_hand_count + post_deck_count + post_discard_count;

        post_hand_treasure = count_cards_of_type(hand, post_hand_count, treasure_t);
        post_deck_treasure = count_cards_of_type(deck, post_deck_count, treasure_t);
        post_discard_treasure = count_cards_of_type(discard, post_discard_count, treasure_t);
        post_total_treasure = post_hand_treasure + post_deck_treasure + post_discard_treasure;

        post_game_total_count = get_total_card_counts(testState, post_game_counts);

        sput_fail_unless(init_game_total_count == post_game_total_count,
                "total cards in game same after play [initial: %d | post: %d]\n",
                init_game_total_count, post_game_total_count);
        sput_fail_unless(init_total_count == post_total_count,
                "player's total cards same after play [initial: %d | post: %d]\n",
                init_total_count, post_total_count);
        sput_fail_unless(init_total_treasure == post_total_treasure,
                "player's total treasure same after play [initial: %d | post: %d]\n",
                init_total_treasure, post_total_treasure);
        sput_fail_unless(post_discard_treasure == init_discard_treasure || post_discard_treasure == 0,
                "didn't discard any treasure cards [initial: %d | post: %d]",
                init_discard_treasure, post_discard_treasure);
        sput_fail_unless(post_hand_treasure == init_hand_treasure + treasure_increase,
                "at most 2 more treasure cards in hand [initial in deck: %d | initial in discard: %d | initial in hand: %d |\n"
                "\tnow in deck: %d | now in discard: %d | now in hand: %d]",
                init_deck_treasure, init_discard_treasure, init_hand_treasure,
                post_deck_treasure, post_discard_treasure, post_hand_treasure);
    }
}

int main(int argc, char *argv[])
{
    // Initialize game state
    testState = newGame();

    SelectStream(DEFAULT_STREAM);
    PlantSeeds(-1);

    sput_start_testing();
    sput_enter_suite(__FILE__);

    sput_run_test(test_card);
    sput_finish_testing();

    free(testState);

    return sput_get_return_value();
}
